#include <QProgressDialog>
#include <QMessageBox>
#include <QThread>
#include "peakfilegeneratorthread.h"
#include "datafile.h"
//
DataFile::DataFile( const QString & name )
	: QFile( name ), dataType(uint8_t), channels(0), sampleRate(1), dataTypeSize(1),
	byteOrder(QDataStream::LittleEndian), blockSize(-1), levels(-1)
{
}
//
DataFile::DataFile( QObject * parent )
	: QFile( parent ), dataType(uint8_t), channels(0), sampleRate(1), dataTypeSize(1),
	byteOrder(QDataStream::LittleEndian), blockSize(-1), levels(-1)
{
}
//
DataFile::DataFile( const QString & name, QObject * parent )
	: QFile( name, parent ), dataType(uint8_t), channels(0), sampleRate(1), dataTypeSize(1),
	byteOrder(QDataStream::LittleEndian), blockSize(-1), levels(-1)
{
}
//
DataFile::~DataFile()
{
	qDebug( ">> DataFile::~DataFile()" );
}
//
void DataFile::setDataType(DataTypes value)
{
	dataType = value;
	switch( dataType )
	{
		case int8_t:
			dataTypeSize = sizeof(qint8);
			break;
		case uint8_t:
			dataTypeSize = sizeof(quint8);
			break;
		case int16_t:
			dataTypeSize = sizeof(qint16);
			break;
		case uint16_t:
			dataTypeSize = sizeof(quint16);
			break;
		case int32_t:
			dataTypeSize = sizeof(qint32);
			break;
		case uint32_t:
			dataTypeSize = sizeof(quint32);
			break;
		case float_t:
			dataTypeSize = sizeof(float);
			break;
		case double_t:
			dataTypeSize = sizeof(double);
			break;
	}
}
//
template <class T>
qint64 DataFile::_readSamples( QwtCPointerData *& data, qint64 start, qint64 maxSamples, int channel )
{
	if( maxSamples <= 0 )
		return -1;
	if( !isOpen() )
		if( !open(QIODevice::ReadOnly) )
		{
			QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można otworzyć pliku!") );
			return -1;
		}
	if( channel >= channels || channel < 0 )
		channel = 0;
	qint64 startPos = ( start * channels + channel ) * dataTypeSize;
	if( startPos >= size() )
		return -1;

	double * x = new double[maxSamples];
	if( !x )
		return -1;
	double * y = new double[maxSamples];
	if( !y ) {
		delete[] x;
		return -1;
	}

	T sample;
	QDataStream stream( this );
	stream.setByteOrder( byteOrder );
	seek( startPos );
	qint64 i;
	for( i = 0; i < maxSamples && !atEnd(); i++ )
	{
		stream >> sample;
		y[i] = (double)sample;
		x[i] = start + i;
		stream.skipRawData( dataTypeSize*(channels-1) );
	}
	data = new QwtCPointerData( x, y, i );
	if( !data ) {
		delete[] x;
		delete[] y;
		return -1;
	}
	return i;
}
//
bool DataFile::validatePeakFile()
{
	peakFile.setFileName( fileName() + ".pks" );
	if( !peakFile.exists() ) {
		return ( generatePeakFile() != -1 ) ? true : false;
	} else {
		QFile pf( peakFile.fileName() );
		if( !pf.open(QIODevice::ReadOnly) )
		{
			QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można otworzyć pliku .pks!\n( validatePeakFile() )") );
			return false;
		}
		QDataStream ps( &pf );
		int dType;//, dChannels, dBlockSize, dLevels;
		ps >> dType;// >> dLevels >> dBlockSize >> dChannels;
		pf.close();
		if( dType != dataType )
			return ( generatePeakFile() != -1 ) ? true : false;
	}
	return true;
}
//
bool DataFile::openPeakFile()
{
	if( !peakFile.isOpen() ) {
		if( !validatePeakFile() ) {
			return false;
		}
		if( !peakFile.open(QIODevice::ReadOnly) )
		{
			QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można otworzyć pliku .pks!") );
			return false;
		}
		QDataStream ps( &peakFile );
		int type;
		ps >> type >> levels >> blockSize;// >> channels;
		dataType = (DataTypes)type;
		
	}
	peakFile.seek(0);
	return true;
}
//
template <class T>
qint64 DataFile::_readPeaks( CPointerPeakData *& buff, qint64 start, qint64 count, int channel, int level )
{
	QDataStream peakStream( &peakFile );
	peakFile.seek( 4*sizeof(int) + (level-1)*(sizeof(qint64)));
	qint64 levelPosition = 0, levelSize = 0;
	peakStream >> levelPosition >> levelSize;
	Q_ASSERT( levelPosition > 0 );

	int scale = level*blockSize;

	start /= scale;
	if( start >= levelSize )
		return -1;
	qint64 begin = levelPosition + (start*channels+channel)*2*sizeof(T);
	if( begin >= size() )
		return -1;
	
	count /= scale;
	double * x = new double[count];
	if( !x )
		return -1;
	QwtDoubleInterval * y = new QwtDoubleInterval[count];
	if( !y ) {
		delete[] x;
		return -1;
	}

	T min, max;
	peakFile.seek( begin );
	qint64 i;
	for( i = 0; i < count && start+i < levelSize && !peakFile.atEnd(); i++ )
	{
		peakStream >> min >> max;
		y[i].setInterval( (double)min, (double)max );
		x[i] = (start + i) * scale;
		peakStream.skipRawData( 2*sizeof(T)*(channels-1) );
	}
	buff = new CPointerPeakData( x, y, i );
	if( !buff ) {
		delete[] x;
		delete[] y;
		return -1;
	}
	return i;
}
//
qint64 DataFile::readPeaks( CPointerPeakData *& buff, qint64 start, qint64 count, int channel, float zoom )
{
	if( channel < 0 || channel >= channels || start < 0 || start >= samplesCount() )
		return -1;
	if( !openPeakFile() )
		return -1;
	int level;
	for( level = 0; zoom >= blockSize; level++ ) {
		zoom /= blockSize;
	}
	level = qMin( level, levels ) ;
	Q_ASSERT( level > 0 );
	switch( dataType )
	{
		case uint8_t:
			return _readPeaks<quint8>( buff, start, count, channel, level );
		case int8_t:
			return _readPeaks<qint8>( buff, start, count, channel, level );
		case uint16_t:
			return _readPeaks<quint16>( buff, start, count, channel, level );
		case int16_t:
			return _readPeaks<qint16>( buff, start, count, channel, level );
		case uint32_t:
			return _readPeaks<quint32>( buff, start, count, channel, level );
		case int32_t:
			return _readPeaks<qint32>( buff, start, count, channel, level );
		case float_t:
			return _readPeaks<float>( buff, start, count, channel, level );
		case double_t:
			return _readPeaks<double>( buff, start, count, channel, level );
		default:
			return -1;
	}
}
//
qint64 DataFile::readSamples(QwtCPointerData *& buff, qint64 start, qint64 maxSamples, int channel)
{
	switch( dataType )
	{
		case uint8_t:
			return _readSamples<quint8>( buff, start, maxSamples, channel );
		case int8_t:
			return _readSamples<qint8>( buff, start, maxSamples, channel );
		case uint16_t:
			return _readSamples<quint16>( buff, start, maxSamples, channel );
		case int16_t:
			return _readSamples<qint16>( buff, start, maxSamples, channel );
		case uint32_t:
			return _readSamples<quint32>( buff, start, maxSamples, channel );
		case int32_t:
			return _readSamples<qint32>( buff, start, maxSamples, channel );
		case float_t:
			return _readSamples<float>( buff, start, maxSamples, channel );
		case double_t:
			return _readSamples<double>( buff, start, maxSamples, channel );
		default:
			return -1;
	}
}
//
qint64 DataFile::samplesCount()
{
	if(channels<=0 && dataTypeSize<=0 ) return -1;
	if( !isOpen() )
		if( !open(QIODevice::ReadOnly) )
		{
			QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można otworzyć pliku!") );
			return -1;
		}
	return size()/channels/dataTypeSize;
}
//
int DataFile::generatePeakFile()
{
	qint64 samples = samplesCount();
	if( samples <= 0 )
		return -1;
	if( !isOpen() )
		if( !open(QIODevice::ReadOnly) )
		{
			QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można otworzyć pliku!") );
			return -1;
		}
	QFile pfile( fileName()+".pks" );
	if( !pfile.open( QIODevice::ReadWrite|QIODevice::Truncate ) )
	{
		QMessageBox::warning( 0, trUtf8( "Błąd"), trUtf8("Nie można utworzyć pliku .pks!") );
		return -1;
	}

	const int blocksize = 32;

	PeakFileGeneratorThread genThread( this, &pfile, samples, channels,
		blocksize, byteOrder, dataType );
	genThread.start();
	
	QProgressDialog pd( trUtf8("Generowanie pliku podglądu"),
		trUtf8("Anuluj"), 0, 100, (QWidget *)parent() );
	connect( &genThread, SIGNAL(progress(int)), &pd, SLOT(setValue(int)));
//	connect( &pd, SIGNAL(canceled()), &genThread, SLOT(cancel()));
	pd.exec();
	if( pd.wasCanceled() ) {
		genThread.cancel();
		qDebug( "cancelled" );
	}

	genThread.wait();
	qDebug( "done" );

	pfile.close();
	return 0;
}
//

